package run.siyuan.manag.util;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import run.siyuan.manag.security.JwtAuthenticationToken;
import run.siyuan.manag.security.GrantedAuthorityImpl;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @program: sy-auth-manag
 * @description:
 * @author: SY
 * @create: 2021-01-14 16:32
 **/
@Slf4j
public class JwtTokenUtil {

    /**
     * 用户名称
     */
    private static final String USERNAME = Claims.SUBJECT;
    /**
     * 创建时间
     */
    private static final String CREATED = "created";
    /**
     * 权限列表
     */
    private static final String AUTHORITIES = "authorities";
    /**
     * 密钥
     */
    private static final String SECRET = "abcdefgh";
    /**
     * 有效期12小时
     */
    private static final long EXPIRE_TIME = 12 * 60 * 60 * 1000;


    /**
     * 根据请求获取令牌，根据令牌获取登入信息
     *
     * @param request
     * @return
     */
    public static Authentication getAuthenticationFromToken(HttpServletRequest request) {
        Authentication authentication = null;
        // 获取 token
        String token = getToken(request);
        if (ObjectUtil.isNotEmpty(token)) {
            // 请求令牌不能为空
            if (ObjectUtil.isEmpty(SecurityUtil.getAuthentication())) {
                Claims claims = getClaimsFromToken(token);
                if (ObjectUtil.isEmpty(claims)) {
                    return null;
                }
                String username = claims.getSubject();
                if (StrUtil.isEmpty(username)) {
                    return null;
                }
                // 校验token
                if (isTokenExpired(token)) {
                    return null;
                }
                // authors 类型 List<Map<String,Object>>
                Object authors = claims.get(AUTHORITIES);
                log.info("获取令牌信息 authors:{}", authors.toString());
                List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
                if (ObjectUtil.isNotEmpty(authors) && authors instanceof List) {
                    for (Object object : (List) authors) {
                        authorities.add(new GrantedAuthorityImpl(((Map) object).get("authority").toString()));
                    }
                }
                authentication = new JwtAuthenticationToken(username, null, authorities, token);
            } else {
                if (validateToken(token, SecurityUtil.getUsername())) {
                    authentication = SecurityUtil.getAuthentication();
                }
            }

        }
        return authentication;

    }

    /**
     * 验证令牌
     *
     * @param token
     * @param username
     * @return
     */
    private static boolean validateToken(String token, String username) {
        String userName = getUsernameFromToken(token);
        return (StrUtil.isNotBlank(username) && StrUtil.isNotBlank(userName) && username.equals(userName)) && !isTokenExpired(token);
    }

    /**
     * 校验token是否过期
     *
     * @param token
     * @return
     */
    private static boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            log.error("校验token是否过期 异常:{}", ExceptionUtil.getMessage(e));
            return false;
        }
    }

    /**
     * 根据 令牌 获取用户名
     *
     * @param token
     * @return
     */
    private static String getUsernameFromToken(String token) {
        String username = null;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            log.error("根据 令牌 获取用户名 异常:{}", ExceptionUtil.getMessage(e));
        }
        return username;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token
     * @return
     */
    private static Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            log.error("从令牌中获取数据声明 异常:{}", ExceptionUtil.getMessage(e));
        }
        return claims;
    }

    /**
     * 获取请求头中的token
     *
     * @param request
     * @return
     */
    public static String getToken(HttpServletRequest request) {
        log.info("获取请求头中的token");
        String token = request.getHeader("Authorization");
        String tokenHead = "Bearer ";
        if (StrUtil.isEmpty(token)) {
            token = request.getHeader("token");
        } else if (token.contains(tokenHead)) {
            token = StrUtil.subSuf(token, tokenHead.length());
        }
        if (StrUtil.isEmpty(token)) {
            token = null;
        }
        return token;
    }

    /**
     * 生成令牌
     *
     * @param authentication
     * @return
     */
    public static String generateToken(Authentication authentication) {
        HashMap<String, Object> claims = new HashMap<>(3);
        claims.put(USERNAME, SecurityUtil.getUsername(authentication));
        claims.put(CREATED, new Date());
        claims.put(AUTHORITIES, authentication.getAuthorities());
        return generateToken(claims);
    }

    private static String generateToken(Map<String, Object> cliams) {
        Date expirationDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        return Jwts.builder().setClaims(cliams).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512,SECRET).compact();

    }

}
